home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / gnustuff / tos / gnulib / libsrc98.zoo / unx2dos.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-06-24  |  16.6 KB  |  662 lines

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <ctype.h>
  4. #include <osbind.h>
  5. #include <device.h>
  6. #include <types.h>
  7. #include <dirent.h>
  8. #include "symdir.h"
  9. #include "lib.h"
  10. #ifndef _COMPILER_H
  11. #include <compiler.h>
  12. #endif
  13.     
  14. #ifndef NAME_MAX
  15. #  include <limits.h>
  16. #endif
  17.     
  18. #ifndef _LIB_NAME_MAX
  19. #  define _LIB_NAME_MAX NAME_MAX
  20. #endif
  21.     
  22. #ifndef TRUE
  23. #define TRUE 1
  24. #define FALSE 0
  25. #endif
  26.     
  27. char _lOK;            /* symbolic links are OK            */
  28. char *_lDIR = ".dir";        /* name of symbolic link directory file    */
  29. char _lAUTO;            /* make automatic symbolic links        */
  30. char _lHIDE;            /* hide the _lDIR file from searches        */
  31.  
  32. char _tCASE;            /* translate filenames to lower case       */
  33. char _tSLASH;            /* '/' seperates directories like '\' does */
  34. char _tDOTS;            /* if != 0, translate '.' into this        */
  35. char _tUNLIMITED;        /* filenames are not restricted to 8.3       */
  36. char _tDEV;            /* allow /dev/filename for special files   */
  37. char _tROOT;            /* use this drive as default root directory */
  38.  
  39. /* translations to assume if there is no UNIXMODE environment variable
  40.  * it's assumed that the user will set this via a definition
  41.  *    char *_default_unixmode = whatever
  42.  * if no such definition is present (i.e. _default_unixmode is 0) then
  43.  * "/d" is assumed
  44.  */
  45.  
  46. char *_default_unixmode;
  47. int _unixmode = 1;    /* this will go away someday */
  48.  
  49. /*
  50.  * _set_unixmode(mode): set Unix emulation modes. Normally "mode" will come
  51.  * from the environment variable UNIXMODE. These settings should only be
  52.  * changed at the explicit request of the user!
  53.  * The characters in "mode" have the following meanings:
  54.  *     b    Open all files in binary mode (no CR, only LF).
  55.  *    c    Assume case is already significant to the operating system.
  56.  *    d    Allow special file names like /dev/console.
  57.  *    r drv    Filenames starting with '/' are rooted from this drive.
  58.  *    u    Assume the OS allows unlimited length file names already.
  59.  *    /    Allow '/' as a directory seperator.
  60.  *    . char    Translate extra dots in a filename to 'char'.
  61.  *    L    Allow symbolic links.
  62.  *
  63.  * The following characters are meaningful only if 'L' is present.
  64.  *    A    Automatically create symbolic links for files whose names
  65.  *        are changed by the _unx2dos routine (e.g. filenames that
  66.  *        don't match the TOS 8 character + 3 character extension
  67.  *        rule). Such automatic symbolic links are much "tighter"
  68.  *        than normal symbolic links, and are effectively aliases
  69.  *        for the old name.
  70.  *    H    Hide the .dir file from directory searches. Explicit
  71.  *        requests for it (e.g. unlink()) still find it, though.
  72.  */
  73.  
  74. static int _adjust __PROTO((char *name, char *result));
  75. static int _canon __PROTO((char *base, char *path, char *result, int level));
  76.  
  77. void _set_unixmode(mode)
  78. char *mode;
  79. {
  80.     char c;
  81.     
  82.     if (!mode && !(mode = _default_unixmode)) {
  83. #if 0
  84.     /* this is what we will eventually do */
  85.     mode = "/";
  86. #else
  87.     /* compatibility with older versions of the library */
  88.     switch(_unixmode) {
  89.       case 0: mode = ""; break;
  90.       case 1: mode = "/d"; break; /* 'd' added to default */
  91.       case 2: mode = "/.,"; break;
  92.       default: mode = "/.,LAHd"; break;
  93.     }
  94. #endif
  95.     }
  96.     _tSLASH = _tDOTS = _tUNLIMITED = _tDEV = FALSE;
  97.     _tCASE = TRUE;
  98.     _tROOT = 0;
  99.     _lOK = _lHIDE = _lAUTO = FALSE;
  100.     
  101.     while (c = *mode++) {
  102.     switch(c) {
  103.       case 'b': _binmode(TRUE); break;
  104.       case 'c': _tCASE = FALSE; break;
  105.       case 'd': _tDEV = TRUE; break;
  106.       case 'u': _tUNLIMITED = TRUE; break;
  107.       case '/': _tSLASH = TRUE; break;
  108.       case 'L': _lOK = TRUE; break;
  109.       case 'A': _lAUTO = TRUE; break;
  110.       case 'H': _lHIDE = TRUE; break;
  111.       case '.':
  112.         if (*mode && !isalpha(*mode))
  113.         _tDOTS = *mode++;
  114.         break;
  115.       case 'r':
  116.         if (*mode && isalpha(*mode))
  117.         _tROOT = *mode++;
  118.       default:
  119.         break;
  120.     }
  121.     }
  122. }
  123.  
  124. /*
  125.  * _uniquefy(name): change name so that it's not the name of any existing
  126.  * file. If the file already exists, then we try changing the file's
  127.  * extension in the following ways:
  128.  * put '$' as the second character
  129.  * put '0'-'9' as the last character (with '$' still in second place).
  130.  * put 'A'-'Z' as the last character (with '$' still in second place).
  131.  * if all of the above fail, put '$$' in the last two characters and
  132.  * give up.
  133.  */
  134.  
  135. void
  136.     _uniquefy(dos)
  137. char *dos;
  138. {
  139.     char *ext, c;
  140.     struct dirent *_do_stat(char *);
  141.     
  142.     if (!_do_stat(dos))    /* file not found, so "dos" is unique */
  143.     return;
  144.     
  145.     /* make 'ext' point to the (last two characters of) the extension */
  146.     
  147.     ext = strrchr(dos, '\\'); if (!ext) ext = dos;
  148.     ext = strrchr(ext, '.');
  149.     if (ext && ext[1]) {
  150.     ext+=2;
  151.     if (!*ext) {
  152.         ext[0] = 0; ext[1] = 0;
  153.     }
  154.     }
  155.     else {
  156.     for(ext=dos; *ext; ext++);
  157.     strcpy(ext, ".$0"); ext++;
  158.     }
  159.     
  160.     *ext = '$';
  161.     if (!_do_stat(dos))
  162.     return;
  163.     
  164.     ext[2] = 0;
  165.     for (c = '0'; c <= '9'; c++) {
  166.     ext[1] = c;
  167.     if (!_do_stat(dos))
  168.         return;
  169.     }
  170.     for (c = 'A'; c <= 'Z'; c++) {
  171.     ext[1] = c;
  172.     if (!_do_stat(dos))
  173.         return;
  174.     }
  175.     /* at this point we're sunk; punt and try '$$' in the extension */
  176.     ext[1] = '$';
  177. }
  178.  
  179. /*
  180.  * adjust(name): adjusts a directory entry to be acceptable to DOS.
  181.  * if _tCASE is on, it is converted to upper case.
  182.  * if _tDOTS is on, '.' is converted to _tDOTS wherever necessary to avoid
  183.  *    conflicts with the DOS 8.3 naming convention.
  184.  * unless _tUNLIMITED is set, only an 8 character base name and 3 character
  185.  *    extension are kept.
  186.  * returns: _NM_CHANGE if the name has been transformed in some irrevocable way
  187.  *    i.e. if dos2unx cannot recover the same name
  188.  *          _NM_OK otherwise
  189.  */
  190.  
  191. static int
  192.     _adjust(name, result)
  193. char *name;
  194. char *result;
  195. {
  196.     char tmp[_LIB_NAME_MAX];
  197.     char *n, *eos, *lastdot = 0;
  198.     int count, change;
  199.     
  200. #ifdef DEBUG
  201.     printf("_adjust(%s)", name); fflush(stdout);
  202. #endif
  203.     strcpy(tmp, name);
  204.     change = 0;
  205.     
  206.     /* first, do case and dot conversion */
  207.     for (n = tmp; *n; n++) {
  208.     if (_tDOTS) {
  209.         if(*n == '.')
  210.         *n = _tDOTS;
  211.         else if (*n == _tDOTS)
  212.         change++;
  213.     }
  214.     if (_tCASE) {
  215.         if (islower(*n))
  216.         *n = toupper(*n);
  217.         else if (isupper(*n))
  218.         change++;
  219.     }
  220.     }
  221.     
  222.     eos = n;    /* end of the "tmp" string */
  223.     
  224.     /* if dots were converted, change the last one possible back into a '.'
  225.      * e.g. if _tDOTS == '_', file.c.Z -> file_c_Z -> file_c.Z
  226.      */
  227.     if (_tDOTS && !_tUNLIMITED) {
  228.     for (n = tmp; *n; n++) {
  229.         if (*n == _tDOTS) {
  230.         if ( (eos - n) <= 4 && (n - tmp) > 8 ) {
  231.             *n = '.';
  232.             goto dot_was_set;
  233.         }
  234.         else lastdot = n;
  235.         }
  236.     }
  237.     /* if we found no good place, and if the name is too long to fit without
  238.      * an extension, we just put the period in the last possible place
  239.      */
  240.     if (lastdot && (n - tmp > 8 || eos - lastdot <= 4))
  241.         *lastdot = '.' ;
  242.     }
  243.     
  244.   dot_was_set:
  245.     /*
  246.      * here we enforce the 8 character name + 3 character extension rule
  247.      */
  248.     if (_tUNLIMITED)
  249.     strcpy(result, name);
  250.     else {
  251.     eos = result;
  252.     n = tmp;
  253.     for (count = 0; count < 8; count++) {
  254.         if (!*n || *n == '.') break;
  255.         *eos++ = *n++;
  256.     }
  257.     while (*n && *n != '.') {
  258.         change++; n++;
  259.     }
  260.     if (*n == '.') *eos++ = *n++;
  261.     for (count = 0; count < 3; count++) {
  262.         if (!*n) break;
  263.         *eos++ = *n++;
  264.     }
  265.     *eos++ = 0;
  266.     change += strlen(n);
  267.     }
  268. #ifdef DEBUG
  269.     printf("->(%s)\n", result);
  270. #endif
  271.     return change ? _NM_CHANGE : _NM_OK;
  272. }
  273.  
  274. /*
  275.  * _canon(base, path, result, level):
  276.  * find a canonical form for the given path, based in the directory "base"
  277.  * (e.g. the current directory); the result is copied into "result".
  278.  * "level" is the level of recursion, and is used to prevent infinite
  279.  * loops in symbolic links.
  280.  *
  281.  * "base" must already be in a canonical form; the return value from the
  282.  * GEMDOS Dgetpath() call is almost suitable, but the drive letter must
  283.  * be prefixed to it. No checking of "base" is done.
  284.  * Note that "base" and "result" may be pointers to the same array!
  285.  *
  286.  * returns:
  287.  *    _NM_LINK    if the last component is a symbolic link
  288.  *    _NM_OK    if the name of the last component is recoverable by _dos2unx
  289.  *    _NM_CHANGE otherwise
  290.  *
  291.  * Also, the global variables __link_path and __link_name are set
  292.  * to the path of the last filename component, and the name of
  293.  * the file, respectively (the canonical form of the file name
  294.  * to is returned in result, and will NOT be __link_path\__link_name if
  295.  * __link_name was a symbolic link). If it was a symbolic link, __link_flags
  296.  * is set to the flags associated with the link (e.g. whether it was
  297.  * auto-created), and __link_to is set to the link contents, which were
  298.  * followed in the course of creating the canonical name.
  299.  *
  300.  * All this is done so that functions like "creat" and "rename" that need
  301.  * access to the path and "real" name of the file (if it was a symbolic link)
  302.  * don't have to duplicate work we've already done.
  303.  */
  304.  
  305. #define DIRSEP(p) ((p) == '\\' || (_tSLASH && (p) == '/'))
  306. #define MAXRECURSE 12
  307.  
  308. char __link_path[FILENAME_MAX], __link_name[_LIB_NAME_MAX],
  309.     __link_to[FILENAME_MAX];
  310. int __link_flags = 0;
  311.  
  312. static int
  313.     _canon(base, path, result, level)
  314. char *base, *path;
  315. char *result;
  316. int level;
  317. {
  318.     char tmp[_LIB_NAME_MAX], name[_LIB_NAME_MAX];
  319.     SYMDIR *dir;
  320.     SYMENTRY *ent;
  321.     char *n;
  322.     int found, change = _NM_OK, needschange = _NM_OK;
  323.     
  324.     /* FIX_ME: we really ought to flag an error of some sort here */
  325.     if (level > MAXRECURSE) {
  326.     return _NM_OK;
  327.     }
  328.     
  329. #ifdef DEBUG
  330.     printf("_canon: [%s] + [%s]\n", base, path);
  331. #endif
  332.     if (!*path) {
  333.     if (result != base)
  334.         strcpy(result, base);
  335.     return _NM_OK;
  336.     }
  337.     
  338.     /* check for paths relative to root of current drive         */
  339.     /* if _tROOT is set, then such paths should start at _tROOT     */
  340.     
  341.     if (DIRSEP(path[0])) {
  342.     if (result != base)
  343.         strcpy(result, base);
  344.     if (_tROOT) {
  345.         if (_tCASE)
  346.         result[0] = toupper(_tROOT);
  347.         else
  348.         result[0] = _tROOT;
  349.     }
  350.     result[2] = 0;    /* the drive is already in the first part */
  351.     path++;
  352.     }
  353.     /* check for absolute paths with drive letter given */
  354.     else if (path[1] == ':') {
  355.     result[0] = _tCASE ? toupper(path[0]) : path[0];
  356.     result[1] = ':';
  357.     result[2] = 0;
  358.     path += 2;
  359.     if (DIRSEP(path[0])) {
  360.         path++;
  361.     }
  362.     }
  363.     else if (result != base)
  364.     strcpy(result, base);
  365.     
  366.     /* now path is relative to what's currently in "result" */
  367.     
  368.     while(*path) {
  369.     /* get next name in path */
  370.     n = name;
  371.     while (*path && !DIRSEP(*path))
  372.         *n++ = *path++;
  373.     *n++ = 0;
  374.     if (*path) path++;
  375.     change = _NM_OK;    /* assume this is a regular name */
  376.     
  377.     /* check for "." and ".." */
  378.     if (!strcmp(name, ".")) continue;
  379.     if (!strcmp(name, "..")) {
  380.         n = strrchr(result, '\\');
  381.         if (n) *n = 0;
  382.         continue;
  383.     }
  384.     
  385.     /* see if "name" is a symbolic link */
  386.     found = 0;
  387.     dir = _read_symdir(result);
  388. #ifdef DEBUG
  389.     if (!dir) printf("unx2dos: _read_symdir(%s) failed\n", result);
  390. #endif
  391.     ent = dir ? dir->s_dir : 0;
  392.     
  393.     while (ent) {
  394.         if (!strcmp(ent->linkname, name)) {
  395.         change = _NM_LINK;
  396. #ifdef DEBUG
  397.         printf("...following link (%s)->(%s)\n", name, ent->linkto);
  398. #endif
  399.         
  400.         if (level == 0) {
  401.             strcpy(__link_path, result);
  402.             strcpy(__link_to, ent->linkto);
  403.             __link_flags = ent->flags;
  404.         }
  405.         /*
  406.          * note that _free_symdir caches the directories it frees, so it's OK
  407.          * to use ent->linkto afterwards (otherwise we'd have to strdup() it.
  408.          * Also note that we *do* want to free it before recursively calling
  409.          * ourselves; otherwise it won't be in the cache.
  410.          */
  411.         found = 1;
  412.         _free_symdir(dir);
  413.         /*
  414.          * We should follow normal symbolic links all the way through, since it's
  415.          * OK to have a link to a link.
  416.          * Auto symbolic links, however, must be linked to a real GEMDOS file.
  417.          */
  418.         if (ent->flags & SD_AUTO) {
  419.             strcat(result, "\\");
  420.             _adjust(ent->linkto, tmp);
  421.             strcat(result, tmp);
  422.         }
  423.         else {
  424.             _canon(result, ent->linkto, result, level+1);
  425.         }
  426.         
  427.         break;    /* out of the search */
  428.         }
  429.         /*
  430.          * if the name we're searching for is the target of an automatic link,
  431.          * then we should have been using that name instead. to prevent
  432.          * name conflict problems, flag the name as needing to be changed;
  433.          * _unx2dos can then try to sort out what to do.
  434.          */
  435.         else if (_lAUTO && !strcmp(ent->linkto, name)) {
  436.         if (ent->flags & SD_AUTO)
  437.             needschange = _NM_CHANGE;
  438.         }
  439.         ent = ent->next;
  440.     }
  441.     
  442.     /* if found == 1, then "result" has already been appropriately updated */
  443.     /* otherwise, append "name" to path, adjusting appropriately */
  444.     if (!found) {
  445.         _free_symdir(dir);
  446.         strcat(result, "\\");
  447.         change = _adjust(name, tmp);
  448.         strcat(result, tmp);
  449.         if (needschange) change = needschange;
  450.     }
  451.     }
  452.     if (level == 0)
  453.     strcpy(__link_name, name);
  454.     return change;
  455. }
  456.  
  457. /*
  458.  * translate a Unix style filename to a DOS one 
  459.  * returns:
  460.  * _NM_DEV    if the name was of a device
  461.  * _NM_LINK     if the name is a symbolic link
  462.  * _NM_OK     if the last component can be recovered via _dos2unx
  463.  * _NM_CHANGE    if the last component is irrevocably changed
  464.  */
  465.  
  466. int
  467.     _lib_unx2dos(unx, dos)
  468. const char *unx;
  469. char *dos;
  470. {
  471.     char path[FILENAME_MAX];
  472.     static char _old_unixmode = 1;
  473.     struct _device *d;
  474.     int change;
  475.     
  476.     /* compatibility code: will go away someday soon */
  477.     if (_unixmode != _old_unixmode) {
  478.     _old_unixmode = _unixmode;
  479.     _set_unixmode(NULL);
  480.     }
  481.     
  482.     /* check for a TOS device name */
  483.     if (d = _dev_dosname(unx)) {
  484.     strcpy(dos, unx);
  485.     return _NM_DEV;
  486.     }
  487.     /* get current directory */
  488.     path[0] = Dgetdrv() + 'A';
  489.     path[1] = ':';
  490.     (void)Dgetpath(path+2, 0);
  491.     
  492.     if (_tDEV && !strncmp(unx, "/dev/", 5)) {
  493.     /* check for a unix device name */
  494.     if (d = _dev_unxname(unx+5)) {
  495.         strcpy(dos, d->dosnm);
  496.         return _NM_DEV;
  497.     }
  498.     /* check for a path like /dev/A/somefile */
  499.     else if (isalpha(unx[5]) && (((unx[6] == '/') || (unx[6] == '\\')) || (!unx[6]))) {
  500.         path[0] = unx[5];
  501.         path[2] = 0;
  502.         unx = (unx[6]) ? &unx[7] : &unx[6];
  503.     }
  504.     }
  505.     
  506.     change = _canon(path, (char *)unx, dos, 0);
  507.     /*
  508.      * If automatic symbolic links are on, then we should try to remap weird
  509.      * filenames into new, unique ones.
  510.      */
  511.     if (change == _NM_CHANGE && _lOK && _lAUTO) {
  512.     _uniquefy(dos);
  513.     }
  514.     return change;
  515. }
  516.  
  517. /*
  518.  * translate a DOS style filename to Unix. Note that this function does
  519.  * *NOT* look up symbolic links; for that, call _full_dos2unx.
  520.  * The return value may be given a useful purpose someday;
  521.  * for now it's always 0.
  522.  */
  523.  
  524. int
  525.     _lib_dos2unx(dos, unx)
  526. const char *dos;
  527. char *unx;
  528. {
  529.     char c;
  530. #ifdef DEBUG
  531.     char *oldunx = unx;
  532.     printf("dos2unx(%s)->", dos); fflush(stdout);
  533. #endif
  534.     while(c = *dos++) {
  535.     if (_tSLASH && c == '\\')
  536.         *unx++ = '/';
  537.     else if (_tDOTS && c == _tDOTS)
  538.         *unx++ = '.';
  539.     else if (_tCASE)
  540.         *unx++ = tolower(c);
  541.     else
  542.         *unx++ = c;
  543.     }
  544.     *unx++ = 0;
  545. #ifdef DEBUG
  546.     printf("(%s)\n", oldunx);
  547. #endif
  548.     return 0;
  549. }
  550.  
  551. int
  552.     _full_dos2unx(dos, unx)
  553. char *dos, *unx;
  554. {
  555.     SYMDIR *dir;
  556.     SYMENTRY *ent;
  557.     char *n, *curdir, *lastslash, name[_LIB_NAME_MAX], tmp[_LIB_NAME_MAX];
  558.     char slash;
  559.     
  560.     curdir = dos;
  561.     lastslash = 0;
  562.     slash = _tSLASH ? '/' : '\\';
  563.     if (dos[0] && dos[1] == ':') {
  564.     *unx++ = tolower(*dos); dos++;
  565.     *unx++ = *dos++;
  566.     }
  567.     if (*dos == '\\') {
  568.     *unx++ = slash; lastslash = dos; dos++;
  569.     }
  570.     else if (*dos) {    /* something is wrong -- punt */
  571.     return _dos2unx(curdir, unx);
  572.     }
  573.     while (*dos) {
  574.     n = tmp;
  575.     while (*dos && *dos != '\\') {
  576.         *n++ = *dos++;
  577.     }
  578.     *n++ = 0;
  579.     _dos2unx(tmp, name);
  580.     *lastslash = 0;        /* tie off current directory */
  581.     /* see if the DOS file has a Unix alias */
  582.     dir = _read_symdir(curdir);
  583.     ent = dir ? dir->s_dir : 0;
  584.     while (ent) {
  585.         if ((ent->flags & SD_AUTO) && 
  586.         !strcmp(ent->linkto, name)) {
  587.         strcpy(name, ent->linkname);
  588.         break;
  589.         }
  590.         ent = ent->next;
  591.     }
  592.     _free_symdir(dir);
  593.     for (n = name; *n; n++)
  594.         *unx++ = *n;
  595.     if (*dos)
  596.         *unx++ = slash;
  597.     *lastslash = '\\';    /* restore path */
  598.     lastslash = dos;
  599.     if (*dos) dos++;
  600.     }
  601.     *unx++ = 0;
  602.     return 0;
  603. }
  604.  
  605. /*
  606.  * the stuff below this line is for compatibility with the old unx2dos, and
  607.  * will go away someday
  608.  */
  609.  
  610. /* system default file name mapping function pointers */
  611.  
  612. static fnmapfunc_t ux2dos = _lib_unx2dos;
  613. static fnmapfunc_t dos2ux = _lib_dos2unx;
  614.  
  615. /* set user specified filename mapping function
  616.  *    NULL => use system default mapping function
  617.  */
  618. void fnmapfunc(u2dos, dos2u)
  619. fnmapfunc_t u2dos, dos2u;
  620. {
  621.     ux2dos = (u2dos == NULL) ? _lib_unx2dos : u2dos;
  622.     dos2ux = (dos2u == NULL) ? _lib_dos2unx : dos2u;
  623. }
  624.  
  625. /* mapping functions -- call the mapping functions via pointers */
  626.  
  627. #ifdef __GNUC__
  628. asm(".stabs \"_unx2dos\",5,0,0,__unx2dos"); /* dept of clean tricks */
  629. #else
  630. int __unx2dos(u, d)
  631. const char *u;
  632. char *d;
  633. {
  634.     return (*ux2dos)(u, d);
  635. }
  636. #endif
  637.  
  638. int _unx2dos(u, d)
  639. const char *u;
  640. char *d;
  641. {
  642.     return (*ux2dos)(u, d);
  643. }
  644.  
  645. #ifdef __GNUC__
  646. asm(".stabs \"_dos2unx\",5,0,0,__dos2unx"); /* dept of clean tricks */
  647. #else
  648. int __dos2unx(d, u)
  649. const char *d;
  650. char *u;
  651. {
  652.     return (*dos2ux)(d, u);
  653. }
  654. #endif
  655.  
  656. int _dos2unx(d, u)
  657. const char *d;
  658. char *u;
  659. {
  660.     return (*dos2ux)(d, u);
  661. }
  662.